// Copyright tang.  All rights reserved.
// https://gitee.com/inrgihc/dbswitch
//
// Use of this source code is governed by a BSD-style license
//
// Author: tang (inrgihc@126.com)
// Date : 2020/1/2
// Location: beijing , china
/////////////////////////////////////////////////////////////
package com.sondertara.joya.migration.provider;

import com.sondertara.common.base.Assert;
import com.sondertara.common.base.Valid;
import com.sondertara.joya.core.jdbc.support.DbType;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ProductProviderFactory {

  private static Map<DbType, Class<? extends ProductFactoryProvider>> providers;

  static {
    providers = new ConcurrentHashMap<>();
  }

  public static void register(DbType type, String classPath) {
    log.info("Register product {} by subclass :{} ", type, classPath);
    ClassLoader classLoader = ProductProviderFactory.class.getClassLoader();

    Class<?> clazz = null;
    try {
      clazz = classLoader.loadClass(classPath);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    // Check subclass
    Assert.isTrue(ProductFactoryProvider.class.isAssignableFrom(clazz),
        "Class '{}' is not a subclass of " +
            "class ProductFactoryProvider", clazz.getName());

    // Check exists
    Assert.isFalse(providers.containsKey(type),
        "Exists ProductFactoryProvider: {} ({})",
        type.name(), providers.get(type.name()));

    // Register class
    providers.put(type, (Class) clazz);
  }

  public static ProductFactoryProvider newProvider(DbType type, DataSource dataSource) {
    Class<? extends ProductFactoryProvider> clazz = providers.get(type);
    Assert.notNull(clazz ,
        "Not exists ProductFactoryProvider: {}", type);

    assert ProductFactoryProvider.class.isAssignableFrom(clazz);
    ProductFactoryProvider instance = null;
    try {
      instance = clazz.getDeclaredConstructor(DataSource.class).newInstance(dataSource);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    Assert.isTrue(type.equals(instance.getProductType()),
        "ProductFactoryProvider with type '{}' " +
            "can't be opened by product type '{}'",
        instance.getProductType(), type);
    return instance;
  }

}
